/**HEADER********************************************************************
* 
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2010 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: spi_int_mpc83xx.c$
* $Version : 3.8.3.1$
* $Date    : Feb-13-2012$
*
* Comments:
*
*    This file contains the low level functions for the interrupt driven
*    SPI I/O for the MPC83xx.
*
*END*********************************************************************/

#include "mqx.h"
#include "bsp.h"
#include "io_prv.h"
#include "charq.h"
#include "fio_prv.h"
#include "spi_int_prv.h"
#include "spi_mpc83xx.h"

extern uint_32 _mpc83xx_spi_int_init(IO_SPI_INT_DEVICE_STRUCT_PTR, char_ptr);
extern uint_32 _mpc83xx_spi_int_deinit(MPC83xx_SPI_INIT_STRUCT_PTR, MPC83xx_SPI_INFO_STRUCT_PTR);
extern uint_32 _mpc83xx_spi_int_enable(MPC83xx_SPI_INFO_STRUCT_PTR);
extern void _mpc83xx_spi_int_isr(pointer);
extern uint_32 _mpc83xx_spi_polled_ioctl(MPC83xx_SPI_INFO_STRUCT_PTR,uint_32, pointer);
extern uint_32 _mpc83xx_spi_polled_init(MPC83xx_SPI_INIT_STRUCT_PTR, pointer _PTR_, char_ptr);
extern uint_32 _mpc83xx_spi_polled_deinit(MPC83xx_SPI_INIT_STRUCT_PTR, MPC83xx_SPI_INFO_STRUCT_PTR );

extern uint_32 _mpc83xx_spi_int_rx(IO_SPI_INT_DEVICE_STRUCT_PTR, char_ptr, int_32);
extern uint_32 _mpc83xx_spi_int_tx(IO_SPI_INT_DEVICE_STRUCT_PTR, char_ptr, int_32);

/*FUNCTION*-------------------------------------------------------------------
* 
* Function Name    : _mpc83xx_spi_int_install
* Returned Value   : uint_32 a task error code or MQX_OK
* Comments         :
*    Install an interrupt driven SPI device.
*
*END*----------------------------------------------------------------------*/

uint_32 _mpc83xx_spi_int_install
   (   
      /* [IN] A string that identifies the device for fopen */
      char_ptr identifier,
  
      /* [IN] The I/O init data pointer */
      pointer  init_data_ptr
   )
      
{

   return _io_spi_int_install(identifier,
      (_mqx_uint (_CODE_PTR_)(pointer,char_ptr))_mpc83xx_spi_int_init,
      (_mqx_uint (_CODE_PTR_)(pointer))_mpc83xx_spi_int_enable,
      (_mqx_uint (_CODE_PTR_)(pointer, pointer))_mpc83xx_spi_int_deinit,
      (_mqx_int (_CODE_PTR_)(pointer, char_ptr, int_32))_mpc83xx_spi_int_tx,
      (_mqx_int (_CODE_PTR_)(pointer, char_ptr, int_32))_mpc83xx_spi_int_rx,
      (_mqx_int (_CODE_PTR_)(pointer, _mqx_uint, _mqx_uint_ptr, _mqx_uint))_mpc83xx_spi_polled_ioctl, 
      (pointer)init_data_ptr);

}


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_int_init
* Returned Value   : uint_32 a task error code or MQX_OK
* Comments         :
*    This function initializes the SPI in interrupt mode.
*
*END*********************************************************************/

uint_32 _mpc83xx_spi_int_init
   (
      /* [IN] the interrupt I/O initialization information */
      IO_SPI_INT_DEVICE_STRUCT_PTR int_io_dev_ptr,
       
      /* [IN] the rest of the name of the device opened */
      char                      _PTR_ open_name_ptr
   )
{
   MPC83xx_SPI_INIT_STRUCT_PTR    io_init_ptr;
   MPC83xx_SPI_INFO_STRUCT_PTR    io_info_ptr;
   uint_32                        result;

   io_init_ptr = int_io_dev_ptr->DEV_INIT_DATA_PTR;

   result = _mpc83xx_spi_polled_init((pointer)io_init_ptr, 
      &int_io_dev_ptr->DEV_INFO_PTR, open_name_ptr);
   if (result != MQX_OK) {
      return(result);
   }

   io_info_ptr = int_io_dev_ptr->DEV_INFO_PTR;

   io_info_ptr->VECTOR  = io_init_ptr->VECTOR;

   /* Allocate buffers */
   io_info_ptr->RX_BUFFER = (pointer)_mem_alloc_system(io_init_ptr->RX_BUFFER_SIZE & (~ 1));
   if (NULL == io_info_ptr->RX_BUFFER) 
   {
      _mem_free (int_io_dev_ptr->DEV_INFO_PTR);
      int_io_dev_ptr->DEV_INFO_PTR = NULL;
      return MQX_OUT_OF_MEMORY;
   }
   io_info_ptr->TX_BUFFER = (pointer)_mem_alloc_system(io_init_ptr->TX_BUFFER_SIZE & (~ 1));
   if (NULL == io_info_ptr->TX_BUFFER) 
   {
      _mem_free (int_io_dev_ptr->DEV_INFO_PTR);
      int_io_dev_ptr->DEV_INFO_PTR = NULL;
      _mem_free (io_info_ptr->RX_BUFFER);
      io_info_ptr->RX_BUFFER = NULL;
      return MQX_OUT_OF_MEMORY;
   }
   _mem_set_type(io_info_ptr->RX_BUFFER,MEM_TYPE_IO_SPI_IN_BUFFER);       
   _mem_set_type(io_info_ptr->TX_BUFFER,MEM_TYPE_IO_SPI_OUT_BUFFER);       
   
   /* Setup the interrupt */
   io_info_ptr->OLD_ISR_DATA = _int_get_isr_data(io_info_ptr->VECTOR);
   io_info_ptr->OLD_ISR_EXCEPTION_HANDLER = 
      _int_get_exception_handler(io_info_ptr->VECTOR);
   
   io_info_ptr->OLD_ISR = 
      _int_install_isr(io_info_ptr->VECTOR, _mpc83xx_spi_int_isr, 
      int_io_dev_ptr);

   io_info_ptr->VECTOR  = io_init_ptr->VECTOR;

   return(MQX_OK);

}


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_int_deinit
* Returned Value   : uint_32 a task error code or MQX_OK
* Comments         :
*    This function de-initializes the SPI in interrupt mode.
*
*END*********************************************************************/

uint_32 _mpc83xx_spi_int_deinit
   (
      /* [IN] the interrupt I/O initialization information */
      MPC83xx_SPI_INIT_STRUCT_PTR io_init_ptr,
       
      /* [IN] the address of the device specific information */
      MPC83xx_SPI_INFO_STRUCT_PTR io_info_ptr
   )
{
   volatile MPC83xx_SPI_STRUCT_PTR   spi_ptr;
  
   spi_ptr  = io_info_ptr->DEV_REG_PTR;

   _mpc83xx_spi_polled_deinit(io_init_ptr, io_info_ptr);

   /* Disable interrupts at IPIC level */
   _mpc83xx_disable_interrupt(io_init_ptr->VECTOR);

   /* Disable interrupts at SPI device level */   
   spi_ptr->IMASK = 0; 

   /* Restore old vector */
   _int_install_isr(io_init_ptr->VECTOR, io_info_ptr->OLD_ISR, 
      io_info_ptr->OLD_ISR_DATA);

   /* Release buffers */
   _mem_free(io_info_ptr->RX_BUFFER);
   io_info_ptr->RX_BUFFER = NULL;
   _mem_free(io_info_ptr->TX_BUFFER);
   io_info_ptr->TX_BUFFER = NULL;
   return(MQX_OK);

}


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_int_enable
* Returned Value   : uint_32 a task error code or MQX_OK
* Comments         :
*    This function enables the SPI interrupts mode.
*
*END*********************************************************************/

uint_32 _mpc83xx_spi_int_enable
   (
      /* [IN] the address of the device specific information */
      MPC83xx_SPI_INFO_STRUCT_PTR io_info_ptr
   )
{
   volatile MPC83xx_SPI_STRUCT_PTR   spi_ptr;
  
   spi_ptr  = io_info_ptr->DEV_REG_PTR;

   spi_ptr->IMASK = (MPC83xx_SPI_IEVENT_DNR | 
                     MPC83xx_SPI_IEVENT_OV  | 
                     MPC83xx_SPI_IEVENT_UN  | 
                     MPC83xx_SPI_IEVENT_NE);
                     
     
   spi_ptr->IMASK &= ~MPC83xx_SPI_IEVENT_NF;   

   /* Enable interrupts */
   _mpc83xx_enable_interrupt(io_info_ptr->VECTOR);

   return(MQX_OK);

}


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_int_isr
* Returned Value   : none
* Comments         : 
*   interrupt handler for the SPI I/O interrupts.
*
*************************************************************************/

void _mpc83xx_spi_int_isr
   (
      /* [IN] the address of the device specific information */
      pointer parameter
   )
{
   IO_SPI_INT_DEVICE_STRUCT_PTR      int_io_dev_ptr = parameter;
   MPC83xx_SPI_INFO_STRUCT_PTR       io_info_ptr;
   volatile MPC83xx_SPI_STRUCT_PTR   spi_ptr;
   uint_32                           events, state;
   uint_16                           index, tmp;
   boolean                           tx_and_rx;
   uint_32                           old_interrupt_mask;
   
   tx_and_rx = (((IO_SPI_INT_DEVICE_STRUCT_PTR)parameter)->FLAGS & SPI_FLAG_FULL_DUPLEX) != 0;
   io_info_ptr = int_io_dev_ptr->DEV_INFO_PTR;
   spi_ptr = io_info_ptr->DEV_REG_PTR;

   /* Save interrupt mask, clear and disable events */
   old_interrupt_mask = spi_ptr->IMASK;
   spi_ptr->IMASK = 0x00;
   
   ++io_info_ptr->STATS.INTERRUPTS;

   /* Perform start of interrupt handling */

   events = spi_ptr->IEVENT;
   
   events &= MPC83xx_SPI_IEVENTS_ALL;
   
   /* get any data and clear all ints */
   tmp = (_mqx_int)spi_ptr->RHR;
   spi_ptr->IEVENT = MPC83xx_SPI_IEVENTS_ALL;

   if (events & MPC83xx_SPI_IEVENT_NE) 
   {
      if (io_info_ptr->ONTHEWAY > 0) io_info_ptr->ONTHEWAY--;

      /* If request for receive and receive allowed (full duplex or first byte skipped already) */
      if ((tx_and_rx) || ((1 == io_info_ptr->RECEIVING) && (io_info_ptr->RX_REQUEST > 0)))
      {
         /* Actual receive into buffer, if overflow, incoming bytes are thrown away */
         index = io_info_ptr->RX_IN + 1;
         if (index >= io_info_ptr->INIT.RX_BUFFER_SIZE) index = 0;
         if (index != io_info_ptr->RX_OUT) 
         {
            io_info_ptr->RX_BUFFER[io_info_ptr->RX_IN] = (uint_8)(tmp);
            io_info_ptr->RX_IN = index++;
            if (index >= io_info_ptr->INIT.RX_BUFFER_SIZE) index = 0;

            io_info_ptr->STATS.RX_PACKETS++;
         } 
         else 
         {
            io_info_ptr->STATS.RX_OVERFLOWS++;
         }
         
         if (io_info_ptr->RX_REQUEST)
         {
            if (--io_info_ptr->RX_REQUEST == 0) 
            {
               io_info_ptr->RECEIVING = 0;
            }
         }
      } 

      /* Skipping bytes sent before receive */
      if (io_info_ptr->RECEIVING > 1) io_info_ptr->RECEIVING--;
   }

   if (events & MPC83xx_SPI_IEVENT_NF) 
   {
      spi_ptr->IMASK &= ~MPC83xx_SPI_IEVENT_NF;
      index = io_info_ptr->RX_IN + 1;
      if (index >= io_info_ptr->INIT.RX_BUFFER_SIZE) index = 0;
      
      if ((tx_and_rx) || (io_info_ptr->RECEIVING == 0)) 
      {
         if (io_info_ptr->TX_OUT != io_info_ptr->TX_IN)
         {
               // Actual transmit
               tmp = io_info_ptr->TX_BUFFER[io_info_ptr->TX_OUT++];
               if (io_info_ptr->TX_OUT >= io_info_ptr->INIT.TX_BUFFER_SIZE) io_info_ptr->TX_OUT = 0;
               if(io_info_ptr->INIT.ENDIAN == SPI_DEVICE_BIG_ENDIAN) {
                  /* SPIM[REV] bit set */
                  spi_ptr->THR = tmp;
               }
               else {
                  spi_ptr->THR = (tmp << (16 - io_info_ptr->INIT.CHAR_LEN)) & 0x0000FFFF;
               }
               io_info_ptr->STATS.TX_PACKETS++;
               io_info_ptr->ONTHEWAY++;            
         } 
         else 
         {
            if ((index != io_info_ptr->RX_OUT) && (io_info_ptr->RX_REQUEST > 0))
            {
               // Dummy transmit, setup receive
               spi_ptr->THR = tmp;
               io_info_ptr->ONTHEWAY++;
               io_info_ptr->RECEIVING = io_info_ptr->ONTHEWAY;
            } 
         }
      }
      events = spi_ptr->IEVENT;
   }
   spi_ptr->IMASK = old_interrupt_mask; 
}

/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_int_rx
* Returned Value   : Returns the number of bytes received
* Comments         : 
*   Reads the data into provided array.
*
*END*********************************************************************/
static uint_32 _mpc83xx_spi_int_rx
   (
      /* [IN] The address of the device specific information */
      IO_SPI_INT_DEVICE_STRUCT_PTR int_io_dev_ptr,

      /* [IN] The array characters are to be read from */
      char_ptr                     buffer,
      
      /* [IN] Number of char's to transmit */
      int_32                       size
   )
{
   MPC83xx_SPI_INFO_STRUCT_PTR     io_info_ptr;
   volatile MPC83xx_SPI_STRUCT_PTR spi_ptr;
   MPC83xx_SPI_INIT_STRUCT_PTR     spi_init_ptr;
   uint_32                         num, index;
   uchar_ptr                       rx_ptr;
   uint_32                         old_interrupt_mask;
   
   io_info_ptr  = int_io_dev_ptr->DEV_INFO_PTR;
   spi_ptr = io_info_ptr->DEV_REG_PTR;
   spi_init_ptr = &(io_info_ptr->INIT);

   /* Save interrupt mask, clear and disable events */
   old_interrupt_mask = spi_ptr->IMASK;
   spi_ptr->IMASK = 0x00;

   /* Check whether new data in rx buffer */
   rx_ptr = (uchar_ptr)(io_info_ptr->RX_BUFFER);
   for (num = 0; num < size; num++) 
   {
      index = io_info_ptr->RX_OUT;
      if (index == (volatile uint_32)io_info_ptr->RX_IN) break;
      *buffer++ = rx_ptr[index++];
      if (index >= io_info_ptr->INIT.RX_BUFFER_SIZE) index = 0;
      io_info_ptr->RX_OUT = index;
   }
   index = size - num;

   if (0 != index)
   {
      /* Not enough data, assert chip selects and enable further transfer */
      io_info_ptr->RX_REQUEST = (volatile uint_32)index;
      
      if ((spi_ptr->MR & MPC83xx_SPI_MR_MS) && (io_info_ptr->CS_ENABLED)) {
         io_info_ptr->CS_CALLBACK(io_info_ptr->INIT.CS, FALSE, io_info_ptr->CS_USERDATA);
      }

      spi_ptr->IMASK = old_interrupt_mask; 
      spi_ptr->IMASK |= MPC83xx_SPI_IEVENT_NE;
      spi_ptr->THR = index;
   }
   else
   {
      spi_ptr->IMASK = old_interrupt_mask;    	
   }
   return num;
}

/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_int_tx
* Returned Value   : return number of byte transmitted
* Comments         : 
*   Writes the provided data into trasmit buffer
*
*END*********************************************************************/
static uint_32 _mpc83xx_spi_int_tx
   (
      /* [IN] The address of the device specific information */
      IO_SPI_INT_DEVICE_STRUCT_PTR int_io_dev_ptr,

      /* [IN] The array to store data */
      char_ptr                     buffer,
      
      /* [IN] Number of char's to transmit */
      int_32                       size
   )
{
   MPC83xx_SPI_INFO_STRUCT_PTR     io_info_ptr;
   volatile MPC83xx_SPI_STRUCT_PTR spi_ptr;
   MPC83xx_SPI_INIT_STRUCT_PTR     spi_init_ptr;
   uint_32                         num, index, tmp;
   uchar_ptr                       tx_ptr;
   uint_32                         old_interrupt_mask;
   
   io_info_ptr  = int_io_dev_ptr->DEV_INFO_PTR;
   spi_ptr = io_info_ptr->DEV_REG_PTR;
   spi_init_ptr = &(io_info_ptr->INIT);

   /* Save interrupt mask, clear and disable events */
   old_interrupt_mask = spi_ptr->IMASK;
   spi_ptr->IMASK = 0x00;

   /* Fill new data into transmit buffer */
   tx_ptr = (uchar_ptr)(io_info_ptr->TX_BUFFER);
   index = io_info_ptr->TX_IN;
   for (num = 0; num < size; num++) 
   {
      tmp = index + 1;
      if (tmp >= io_info_ptr->INIT.TX_BUFFER_SIZE) tmp = 0;
      if (tmp == io_info_ptr->TX_OUT) break;
      tx_ptr[index] = *buffer++;
      index = tmp;
   }
   io_info_ptr->TX_IN = index;
   
   if (0 != num) 
   {
      /* At least one byte to transmit, assert chip selects and enable transfer */
      if ((spi_ptr->MR & MPC83xx_SPI_MR_MS) && (io_info_ptr->CS_ENABLED)) {
         io_info_ptr->CS_CALLBACK(io_info_ptr->INIT.CS, FALSE, io_info_ptr->CS_USERDATA);
      }

      spi_ptr->IMASK = old_interrupt_mask; 
      spi_ptr->IMASK &= ~MPC83xx_SPI_IEVENT_NF;   
   }
   else
   {
      spi_ptr->IMASK = old_interrupt_mask;    	
   }
   return num;
}

/* EOF */
